package kline

import (
	"global/libs/structs"
	"global/core"
	"github.com/ngaut/log"
	"global/util"
	"sync"
	"global/constants"
	"github.com/shopspring/decimal"
	"github.com/garyburd/redigo/redis"
	"strconv"
)

type KlineHandleCoin struct {
	ctx 		*core.Context
	coin 		string
	data 		chan *structs.CoinKlineQueue

	wg 			sync.WaitGroup
	mux 		sync.Mutex
}

func NewKlineHandleCoin(
	ctx *core.Context,
	coin string) *KlineHandleCoin {
	return &KlineHandleCoin{
		ctx: 	ctx,
		coin: 	coin,
		data: 	make(chan *structs.CoinKlineQueue, 10)}
}

func (k *KlineHandleCoin) Start() {
	go k.listen()
}

func (k *KlineHandleCoin) listen() {
	for {
		select {
		case d := <-k.data:
			k.handler(d)
		}
	}
}

func (k *KlineHandleCoin) handler(d *structs.CoinKlineQueue) {
	log.Infof("Info: %s", d.CoinName)
	detail := k.getDetail(d)
	if detail == nil { return }

	intervals := k.getIntervals(d.Timestamp)
	analysisedDetails := k.getAnalysisedDetails(intervals)

	conn := k.ctx.GetBaseRedis().Get()
	defer conn.Close()
	p := k.getPrice(conn, detail.To)

	results := make(map[string]structs.SaveKlineOption, 0)
	k.wg.Add(len(intervals))
	for i, v := range intervals {
		go func(period string, t int64) {
			defer k.wg.Done()

			var m sync.Mutex
			m.Lock()
			d, ok := analysisedDetails[i]
			m.Unlock()

			var result []string
			var isNew bool
			if !ok || d == nil || len(d) != 6 {
				result = k.newToString(detail, p)
				isNew = true
			} else {
				result = k.compareToString(detail, d, p)
			}

			k.mux.Lock()
			results[period] = structs.SaveKlineOption{
				IsNew: 	isNew,
				Data: 	result,
			}
			k.mux.Unlock()
		}(i, v)
	}
	k.wg.Wait()

	// save result
	err := core.SaveAnalysisKlineCoinDetail(conn, k.coin, results)
	if err != nil {
		log.Errorf("Error: %s", err)
		return
	}

	// 数据清理
	k.clearOriginalData(conn, detail)
}

func (k *KlineHandleCoin) clearOriginalData(conn redis.Conn, detail *structs.SpiderKline) {
	err := core.RemoveSpiderKlineDetail(conn, detail.Exchange, "1min", detail.From + "_" + detail.To, detail.DateTime)
	if err != nil {
		log.Errorf("Error: %s", err)
	}
}

func (k *KlineHandleCoin) compareToString(
	detail *structs.SpiderKline,
	old []string, p decimal.Decimal) []string {

	tmp := make([]string, 6)
	tmp[0] = old[0]
	tmp[1] = util.GetGreater(old[1], detail.Open)
	tmp[2] = util.GetGreater(old[2], detail.High)
	tmp[3] = util.GetGreater(old[3], detail.Low)
	tmp[4] = util.GetGreater(old[4], detail.Close)
	tmp[5] = util.GetSum(old[5], detail.Volume)

	return tmp
}

func (k *KlineHandleCoin) newToString(detail *structs.SpiderKline, p decimal.Decimal) []string {
	tmp := make([]string, 6)
	o, _ := decimal.NewFromString(detail.Open)
	h, _ := decimal.NewFromString(detail.High)
	l, _ := decimal.NewFromString(detail.Low)
	c, _ := decimal.NewFromString(detail.Close)
	tmp[0] = strconv.Itoa(int(detail.DateTime))
	tmp[1] = o.Mul(p).String()
	tmp[2] = h.Mul(p).String()
	tmp[3] = l.Mul(p).String()
	tmp[4] = c.Mul(p).String()
	tmp[5] = detail.Volume
	return tmp
}

// 获取币种的美元价格，以 decimal 类型显示
func (k *KlineHandleCoin) getPrice(conn redis.Conn, coin string) decimal.Decimal {
	if coin == constants.USDT {
		return decimal.NewFromFloat(1)
	}
	ticker , err := core.GetCoinTicker(conn, coin)
	if err != nil {
		return decimal.Zero
	}
	f, _ := decimal.NewFromString(ticker.USD)
	return f
}

// 获取分析过的记录
func (k *KlineHandleCoin) getAnalysisedDetails(intervals map[string]int64) map[string][]string {
	conn := k.ctx.GetBaseRedis().Get()
	defer conn.Close()
	details, err := core.GetAnalysisKlineDetail(conn, k.coin, intervals)
	if err != nil {
		log.Errorf("Error: %s", err)
		return nil
	}
	return details
}

func (k *KlineHandleCoin) getIntervals(timestamp int64) map[string]int64 {
	intervals := make(map[string]int64)
	for interval := range util.IntervalMap {
		t := util.FormatTimeToInterval(timestamp, interval)
		if t == 0 { continue }
		intervals[interval] = t
	}
	return intervals
}

// 检查是否有存在的已经被分析的数据
func (k *KlineHandleCoin) checkIfHasAnalysisedData()  {
}

// 获取详情
func (k *KlineHandleCoin) getDetail(q *structs.CoinKlineQueue) *structs.SpiderKline {
	conn := k.ctx.GetBaseRedis().Get()
	defer conn.Close()
	detail, err := core.GetSpiderKlineDetail(conn, q.ExchangeName, q.SymbolStr, "1min", q.Timestamp)
	if err != nil {
		log.Errorf("Error: %s")
		return nil
	}
	return detail
}

